
/**
 * var util    = require('util');
 * var watcher = require('watcher');
 */

var $$id = 0;

/**
 * Compiler，实现对模板的编译，提取指令并将vm与视图关联起来
 */
function Compiler(el, vm) {
    // 创建编译节点
    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
	
	// 保存viewModel
    this.$vm = vm;
	
	// 将原生节点拷贝到documentFragment，才开始编译，此处为了dom性能优化，在真正的html减少dom操作
    if (this.$el) {
        this.$fragment = nodeToFragment(this.$el);
        this.init();
        this.$el.appendChild(this.$fragment);
    }
}

Compiler.prototype = {
    init: function() {
        this.compile(this.$fragment);
    },

	// 编译主体，遍历子元素
    compile: function(el) {
        var childNodes = el.childNodes,
            self = this;
		
        [].slice.call(childNodes).forEach(function(node) {
            var text = node.textContent;
            var reg = /\{\{(.*)\}\}/;

			node.$id = $$id++;

            if (self.isElementNode(node)) {
                self.compileElementNode(node);

            } else if (self.isTextNode(node) && reg.test(text)) {
                self.compileText(node, RegExp.$1);
            }

            if (node.childNodes && node.childNodes.length) {
                self.compile(node);
            }
        });
    },
	
	// 编译节点元素，调用相应的指令处理方法
    compileElementNode: function(node) {
        var nodeAttrs = node.attributes,
            self = this;

		// attributes是动态的，要复制到数组里面去遍历
        [].slice.call(nodeAttrs).forEach(function(attr) {
            var attrName = attr.name;
            if (self.isDirective(attrName)) {
                var exp = attr.value;
                var dir = attrName.substring(2);
                
                if (self.isEventDirective(dir)) {
                	// 事件指令
                    compileUtil.eventHandler(node, self.$vm, exp, dir);
                } else {
                	// 普通指令
                    compileUtil[dir] && compileUtil[dir](node, self.$vm, exp);
                }

                node.removeAttribute(attrName);
            }
        });
    },
	
	// 编译文本元素，解析表达式
    compileText: function(node, exp) {
        compileUtil.text(node, this.$vm, exp);
    },

	// 是否指令属性
    isDirective: function(attr) {
        return attr.indexOf('v-') == 0;
    },

	// 是否事件指令
    isEventDirective: function(dir) {
        return dir.indexOf('on') === 0;
    },

	// 是否元素节点
    isElementNode: function(node) {
        return node.nodeType == 1;
    },

	// 是否文本节点
    isTextNode: function(node) {
        return node.nodeType == 3;
    }
};

// 复制节点到文档碎片
function nodeToFragment(node) {
    var fragment = document.createDocumentFragment(),
        child;

    // 将原生节点拷贝到documentFragment
    while (child = node.firstChild) {
    	if(isIgnorable(child)) {
    		// 删除注释节点和换行节点
    		node.removeChild(child);
    	}else {
    		fragment.appendChild(child);
    	}
    }

    return fragment;
}

/*
 * 是否注释节点和换行节点
 */
function isIgnorable(node) {
	var regIgnorable = /^[\t\n\r]+/;
	return (node.nodeType == 8) || ((node.nodeType == 3) && (regIgnorable.test(node.textContent)));
}

/*
 * 指令处理集合
 */
var compileUtil = {
    text: function(node, vm, exp) {
        this.bind(node, vm, exp, 'text');
    },

    html: function(node, vm, exp) {
        this.bind(node, vm, exp, 'html');
    },

	for: function(node, vm, exp) {
		this.bind(node, vm, exp, 'for');
	},

    model: function(node, vm, exp) {
    	if(node.tagName.toLowerCase() == 'input') {
    		switch(node.type) {
    			case 'checkbox':
    				this.bind(node, vm, exp, 'checkbox');
    				var self = this;
    				node.addEventListener('change', function(e) {
    					var target = e.target;
    					var value = target.value || target.$id;
    					var index = vm[exp].indexOf(value);
    					if(target.checked) {
    						vm[exp].push(value);
    					}else {
    						vm[exp].splice(index, 1);
    					}
    				});
    				break;
    			case 'radio':
    				this.bind(node, vm, exp, 'radio');
    				var self = this;
    				node.addEventListener('change', function(e) {
    					var target = e.target;
    					var value = target.value || target.$id;
    					if(target.checked) {
    						vm[exp] = value;
    					}
    				});
    				break;
    			default:
    				this.bind(node, vm, exp, 'model');
					
			        var self = this,
			            val = util.getVMVal(vm, exp);
			        node.addEventListener('input', function(e) {
			        	// 由于上面绑定了自动更新，循环依赖了，中文输入法不能用。这里加入一个标志避开自动update
			        	node.isInputting = true;
			            var newValue = e.target.value;
			            if (val === newValue) {
			                return;
			            }
						
			            util.setVMVal(vm, exp, newValue);
			            val = newValue;
			        });
    		}
    	}
    },

    class: function(node, vm, exp) {
        this.bind(node, vm, exp, 'class');
    },

    bind: function(node, vm, exp, dir) {
        var updaterFn = updater[dir + 'Updater'];

        updaterFn && updaterFn(node, util.getVMVal(vm, exp));

        new Watcher(vm, exp, function(value, oldValue) {
            updaterFn && updaterFn(node, value, oldValue);
        });
    },

    // 事件处理
    eventHandler: function(node, vm, exp, dir) {
        var eventType = dir.split(':')[1],
            fn = vm.$options.methods && vm.$options.methods[exp];

        if (eventType && fn) {
            node.addEventListener(eventType, fn.bind(vm), false);
        }
    }
};

/**
 * 依赖到Watcher的回调函数，这里是实际操作node的地方
 */
var updater = {
    textUpdater: function(node, newValue) {
        node.textContent = typeof newValue == 'undefined' ? '' : newValue;
    },

    htmlUpdater: function(node, newValue) {
        node.innerHTML = typeof newValue == 'undefined' ? '' : newValue;
    },
    
    forUpdater: function(node, newValue) {
    	var arr = newValue,
    		elementNode;
    	
    	if(node.getAttribute('data-node') == 'none') {
    		elementNode = document.createElement('span');
    		node.setAttribute('data-node', 'block');
    		node.appendChild(elementNode);
    	}else {
    		elementNode = node.lastChild;
    		elementNode.innerHTML = '';
    	}
    	
    	[].slice.call(arr).forEach(function(data, index) {
    		var textNode = document.createTextNode('');
    		textNode.textContent = (index + 1) + '.' + data.msg + ' ';
    		elementNode.appendChild(textNode);
    	})
    },

    classUpdater: function(node, newValue, oldValue) {
        var className = node.className;
        className = className.replace(oldValue, '').replace(/\s$/, '');

        var space = className && String(newValue) ? ' ' : '';

        node.className = className + space + newValue;
    },
	
    modelUpdater: function(node, newValue, oldValue) {
    	// 当有输入的时候循环依赖了，中文输入法不能用。这里加入一个标志避开自动update
		if (!node.isInputting) {
			node.value = typeof newValue == 'undefined' ? '' : newValue;
		}
		// 记得要重置标志
		node.isInputting = false;
    },
    
    checkboxUpdater: function(node, newValue, oldValue) {
    	// 处理数组
		var value = node.value || node.$id;
		newValue.forEach(function(data) {
			if (data.indexOf(value) < 0) {
				node.checked = false;
			} else {
				node.checked = true;
			}
		})
    },
   	
    radioUpdater: function(node, newValue, oldValue) {
   		
    }
};
